﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Board
{
    Cell[,] m_Board;
    Dictionary<Vector2Int, CellString> m_CellStringDict;
    List<CellString> m_CellStrings;
    public List<Vector2Int> m_EmptyCells;

    public Cell this[Vector2Int pos] => m_Board[pos.x, pos.y];
    public Cell this[int x, int y] => m_Board[x, y];

    public Board()
    {
        m_Board = new Cell[GameConfig.Width, GameConfig.Height];
        m_CellStringDict = new Dictionary<Vector2Int, CellString>();
        m_CellStrings = new List<CellString>();
        m_EmptyCells = new List<Vector2Int>();

        InitBoard();
    }

    public void InitBoard()
    {
        for (int x = 0; x < GameConfig.Width; x++)
        {
            for (int y = 0; y < GameConfig.Height; y++)
            {
                m_Board[x, y] = new Cell(x, y);
            }
        }

        // connect cell
        for (int x = 0; x < GameConfig.Width; x++)
        {
            for (int y = 0; y < GameConfig.Height; y++)
            {
                List<Cell> connectedCellList = ListPool<Cell>.Claim();
                if (TryGetCell(x - 1, y, out Cell c0)) connectedCellList.Add(c0);
                if (TryGetCell(x - 1, y + 1, out Cell c1)) connectedCellList.Add(c1);
                if (TryGetCell(x, y + 1, out Cell c2)) connectedCellList.Add(c2);
                if (TryGetCell(x + 1, y, out Cell c3)) connectedCellList.Add(c3);
                if (TryGetCell(x + 1, y - 1, out Cell c4)) connectedCellList.Add(c4);
                if (TryGetCell(x, y - 1, out Cell c5)) connectedCellList.Add(c5);
                m_Board[x, y].m_ConnectedCells = connectedCellList.ToArray();
                ListPool<Cell>.Release(connectedCellList);
            }
        }
    }

    public static Board CopyFrom(Board other)
    {
        // deep copy
        Board board = ObjectPool<Board>.Claim();
        board.m_CellStringDict.Clear();
        board.m_CellStrings.Clear();
        board.m_EmptyCells.Clear();

        int cellStringCount = other.m_CellStrings.Count;
        for (int i = 0; i < cellStringCount; i++)
        {
            CellString copyString = CellString.CopyFrom(other.m_CellStrings[i]);
            board.m_CellStrings.Add(copyString);
            foreach (Vector2Int cellPos in copyString.m_CellPositions)
            {
                if (!board.m_CellStringDict.ContainsKey(cellPos))
                {
                    board.m_CellStringDict.Add(cellPos, copyString);
                }
            }
        }

        board.m_EmptyCells.AddRange(other.m_EmptyCells);
        for (int x = 0; x < GameConfig.Width; x++)
        {
            for (int y = 0; y < GameConfig.Height; y++)
            {
                board[x, y].m_Empty = other[x, y].m_Empty;
                board[x, y].m_BelongTo = other[x, y].m_BelongTo;
            }
        }

        return board;
    }

    bool TryGetCell(int x, int y, out Cell cell)
    {
        if (x >= 0 && x < GameConfig.Width && y >= 0 && y < GameConfig.Height)
        {
            cell = m_Board[x, y];
            return true;
        }
        cell = null;
        return false;
    }

    public void Start()
    {
        m_CellStrings.Clear();
        m_CellStringDict.Clear();
        for (int x = 0; x < GameConfig.Width; x++)
        {
            for (int y = 0; y < GameConfig.Height; y++)
            {
                m_Board[x, y].Clear();
                m_EmptyCells.Add(new Vector2Int(x, y));
            }
        }
    }
    public void Release()
    {
        foreach (CellString cellString in m_CellStrings)
        {
            cellString.Release();
        }
        m_CellStrings.Clear();
        m_CellStringDict.Clear();
    }

    public bool TakeStep(Player player, Vector2Int pos, out Player winner)
    {
        winner = Player.None;
        Cell cell = this[pos];
        if (!cell.m_Empty) return false;
        cell.m_Empty = false;
        m_EmptyCells.Remove(cell.m_Pos);
        cell.m_BelongTo = player;
        // check string connect
        CellString connectedString = null;
        foreach (Cell neighbor in cell.m_ConnectedCells)
        {
            if (neighbor.m_BelongTo == player)
            {
                if (m_CellStringDict.TryGetValue(neighbor.m_Pos, out CellString cellString))
                {

                    if (connectedString == null)
                    {
                        cellString.AddCell(cell.m_Pos);
                        connectedString = cellString;
                    }
                    else
                    {
                        if (connectedString != cellString)
                        {
                            // merge strings
                            foreach (Vector2Int cellPos in cellString.m_CellPositions)
                            {
                                connectedString.AddCell(cellPos);
                                m_CellStringDict[cellPos] = connectedString;
                            }
                            // remove merged string
                            m_CellStrings.Remove(cellString);
                            cellString.Release();
                        }
                    }
                }
            }
        }
        if (connectedString == null)
        {
            // 周围没有友方棋子，创建新链
            CellString newString = CellString.Create();
            newString.m_BelongTo = player;
            newString.AddCell(cell.m_Pos);
            m_CellStrings.Add(newString);
            m_CellStringDict.Add(pos, newString);
            // 周围都没有友方子，肯定没赢
            return m_EmptyCells.Count == 0; // 棋盘填满了，就结束
        }
        else
        {
            // 和周围的棋链合并后判断有没有赢
            m_CellStringDict.Add(pos, connectedString);
            if (connectedString.IsComplete)
            {
                winner = connectedString.m_BelongTo;
                return true;
            }
            return m_EmptyCells.Count == 0; // 棋盘填满了，就结束
        }
    }
}
